<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>HotPot 自助火锅</title>
    <link rel="stylesheet" th:href="@{/css/base.css}"/>
    <link rel="stylesheet" th:href="@{/plugins/hp-ui/hp-ui.css}"/>
    <link rel="stylesheet" th:href="@{/css/chat/chat.css}"/>

    <script type="text/javascript" charset="utf-8" th:src="@{/plugins/hp-ui/hp-ui.js}"></script>
    <script type="text/javascript" charset="utf-8" th:src="@{/plugins/vue/vue.js}"></script>
</head>

<body>
<header th:replace="~{fragments/header :: header}">...</header>

<article>
    <div id="app">
        <div class="main">
            <div class="msg-wrapper">
                <div class="header">
                    <span>HOTPOT ONLINE</span><marquee class="notice">{{notice}}</marquee>
                </div>
                <div class="msg-content scrollbar">
                    <div v-for="o in msgList" class="msg-item">
                        <img :src="o.avatar" class="hp-avatar square center"/>
                        <div class="msg">
                            <div class="nickname">{{o.nickname}}</div>
                            <div class="content" v-html="o.msg"></div>
                        </div>
                    </div>
                </div>

            </div>

            <div class="input-wrapper">
                <div class="toolbar">
                    <span title="表情" id="emoji">
                        <img :src="emoji"/>

                        <div id="emoji-board">
                            <div class="emoji-wrapper">
                                <!-- 表情 -->
                                <ul></ul>
                            </div>
                            <!-- 表情面板工具条 -->
                            <div class="toolbar">
                                <ul>
                                    <li class="add">
                                        <img src=""
                                             class="hp-avatar square small"/>
                                        <input class="add-btn" type="file" name="file" id="add-btn">
                                    </li>
                                </ul>
                            </div>

                        </div>

                    </span>
                    <span title="//DODO 截图" class="capture">
                        <img :src="capture" onclick="alert('想用截图？自己去实现')"/>
                    </span>

                </div>

                <div class="input">
                    <!-- div设置contenteditable 模拟textarea，上传图片成功后用img标签生成图片预览插入div就可以预览了-->
                    <div contentEditable="true" class="textarea scrollbar" id="msg" tabindex="-1" onblur="getblur()">
                        <img src="">
                    </div>
                    <button @click="send">发送</button>
                </div>

            </div>
        </div>

        <div class="user-info">
            <div class="user-info-wrapper">
                <div class="header">
                    <span>游客</span>
                </div>
                <div class="user-item scrollbar">
                    <ul v-for="u in userList">
                        <li><img :src="u.avatar" class="hp-avatar square small"/></li>
                        <li class="nickname">{{u.nickname}}</li>
                        <!-- style="display: none" -->
                        <li class="ip">{{u.ip}}</li>
                    </ul>
                </div>
            </div>
        </div>
    </div>
</article>
</body>
<!-- import Vue before Element -->
<!--<script src="vue.js"></script>-->
<script th:inline="javascript">
    var USER_OPEN = 1;     // 用户连接
    var USER_SEND_MSG = 2; // 用户发送消息
    var USER_CLOSE = 3;    // 用户断开连接
    /*<![CDATA[*/
    var SERVER_ADDRESS = [[${server_address}]];
    var SERVER_PORT = [[${server_port}]];
    var AVATAR = [[${avatar}]];
    /*]]>*/
    var v = new Vue({
        el: '#app',
        data: function () {
            let img = '';
            return {
                socket : "",
                nickname: "",
                notice: "",
                userList: [
                    // {avatar: img, nickname: "林冲", ip: "192.168.200.253"},
                    // {avatar: img, nickname: "袁世凯", ip: "192.168.200.254"}
                    ],
                msgList: [
                    // {avatar: img, nickname: "韩信", msg: "this is <img src='" + img + "'/><i>v-once</i>"}
                    ],
                emoji: '',
                capture: ''
            }
        },
        methods: {
            init () {
                // 初始化用户名
                /*<![CDATA[*/
                this.nickname = [[${nickname}]]
                /*]]>*/
                console.log(this.nickname)
                if (typeof (WebSocket) == "undefined") {
                    console.log("遗憾：您的浏览器不支持WebSocket");
                } else {
                    console.log("恭喜：您的浏览器支持WebSocket");

                    //实现化WebSocket对象
                    //指定要连接的服务器地址与端口建立连接
                    //注意ws、wss使用不同的端口。我使用自签名的证书测试，
                    //无法使用wss，浏览器打开WebSocket时报错
                    //ws对应http、wss对应https。
                    this.socket = new WebSocket(["ws://", SERVER_ADDRESS,":", SERVER_PORT ,"/ws"].join(""));
                    //连接打开事件
                    this.socket.onopen = () => {
                        console.log("Socket 已打开");
                        // this.wssend(JSON.stringify({"nickname": this.nickname, "msg_type": USER_OPEN}));
                    };
                    //收到消息事件
                    this.socket.onmessage = (msg) => {
                        // console.log(msg.data);
                        let msgJSON = JSON.parse(msg.data);

                        if (msgJSON.type === USER_OPEN) {
                            // 用户上线提示
                            this.userList.push({
                                avatar : AVATAR,
                                nickname: msgJSON.content.nickname,
                                ip: msgJSON.content.ip})
                            this.notice = msgJSON.content.nickname + " 上线...";

                        } else if (msgJSON.type === USER_SEND_MSG) {
                            // 用户消息提示
                            let message = {
                                avatar: msgJSON.content.avatar,
                                nickname: msgJSON.content.nickname,
                                msg: msgJSON.content.msg}
                            this.msgList.push(message);

                            // 获取消息后，让菜单滚动到底部
                            this.$nextTick(() =>{
                                document.getElementsByClassName("msg-content").item(0).scrollTop = document
                                    .getElementsByClassName("msg-content").item(0).scrollHeight + 40;
                            });
                            // 滚从消息主窗体到底部

                        } else if (msgJSON.type === USER_CLOSE) {
                            let tmp = [];
                            this.userList.forEach((item) => {
                                if (item.ip != msgJSON.content.ip) {
                                    tmp.push(item);
                                }
                            });
                            this.userList = tmp;
                            this.notice = msgJSON.content.nickname + " 下线...";
                        }
                    };
                    //连接关闭事件
                    this.socket.onclose = function() {
                        console.log("Socket已关闭");
                    };
                    //发生了错误事件
                    this.socket.onerror = function() {
                        alert("Socket发生了错误");
                    }

                    //窗口关闭时，关闭连接
                    window.unload = function() {
                        this.socket.close();
                    };

                    // 给输入div绑定键盘事件（按回车键发送消息）
                    document.getElementById("msg").onkeydown = (ev) => {
                        if (ev.keyCode === 13 || ev.key === 'Enter') {this.send()}
                    }

                    document.getElementById("msg").onkeyup = (ev) => {
                        if (ev.keyCode === 13 || ev.key === 'Enter') {
                            let msg = document.getElementById("msg");
                            while(msg.hasChildNodes()) { //当div下还存在子节点时 循环继续
                                msg.removeChild(msg.firstChild);
                            }
                        }
                    }
                }
            },
            send () {
                let msg = document.getElementById("msg");
                if (msg.innerHTML.includes("<img")) {
                    $h.post(url=["http://", SERVER_ADDRESS,":", SERVER_PORT ,"/community/chat"].join(""),
                    data= {avatar: AVATAR, nickname: this.nickname, msg: msg.innerHTML});
                } else {
                    this.socket.send(JSON.stringify({avatar: AVATAR, nickname: this.nickname, msg: msg.innerHTML}));
                }

                while(msg.hasChildNodes()) { //当div下还存在子节点时 循环继续
                    msg.removeChild(msg.firstChild);
                }
                // this.msgList.push({avatar: this.emoji, nickname: nickname, msg: msg.innerHTML})
            }
        },
        mounted: function(){
            this.init();
        },
        destroyed: function () {
            // 销毁监听
            this.socket.close();
        }
    });

</script>


<script>
    function stopBubble(evt) {
        //如果提供了事件对象，则这是一个非IE浏览器
        if ( evt && evt.stopPropagation )
        //因此它支持W3C的stopPropagation()方法
            evt.stopPropagation();
        else
        //否则，我们需要使用IE的方式来取消事件冒泡
            window.event.cancelBubble = true;
    }

    /**
     * 绑定面板与document 的点击事件，使面板具有模态框功能
     */
    function emojiAddEventListener() {
        /**
         * emoji插件开始的地方
         * 使用emoji插件时，需要在触发显示emoji面板的 元素上添加 id="emoji"
         * 同时，需要将该元素设置为 {position: relative;}
         *
         * 例如：
         * <span id="emoji">
         *     <img :src="emoji"/>
         * </span>
         *
         * 这时 emoji 就会被插入 在 id="emoji" 的span 标签的第一个子元素位置上
         * @type {HTMLElement}
         */
        let emoji = document.getElementById("emoji");

        if (emoji) {
            emoji.addEventListener("click", function (evt) {
                stopBubble(evt)
                let emoji_board = document.getElementById("emoji-board");
                if (emoji_board) {
                    emoji_board.style = "display: block";
                }
            })
        }

        // 对 document 绑定 点击事件
        document.addEventListener("click", function (evt) {
            stopBubble(evt)
            // 获取需要 显示或者隐藏 的待处理的 目标控件
            let emoji_board = document.getElementById("emoji-board");
            if (emoji_board) {
                // 在点击整个页面时，如果目标控件控件存在，
                // 判断 被点击的的控件是否被 目标控件包含
                // 如果不被包含，则表示点击到目标控件外头了;
                // 简单地说，目标控件被点中时，ev.target 就是目标控件，
                if (!emoji_board.contains(evt.target)) {
                    emoji_board.style = "display: none";
                }
            }
        })
    }

    /**
     * 添加表情的按钮增加点击事件
     */
    function addBtnAddEventListener() {
        document.getElementById("add-btn").addEventListener("change", function() {
            let fr = new FileReader();
            fr.onload = function(){
                let base64Img = this.result;
                let emojiArr = JSON.parse(localStorage.getItem("emoji"));
                emojiArr.push(base64Img);
                localStorage.setItem("emoji", JSON.stringify(emojiArr));
                // 重新刷新面板
                initEmojiBoard();
            }

            fr.readAsDataURL(this.files[0]);
        })
    }

    /**
     * 初始化表情面板
     */
    function initEmojiBoard() {
        // 先判断是否有本地表情，没有则增加
        if(!localStorage.getItem("emoji")) {
            localStorage.setItem("emoji", JSON.stringify([]));
        }

        let emojiUl = document.getElementById("emoji-board").firstElementChild.firstElementChild;
        while(emojiUl.hasChildNodes()) { //当div下还存在子节点时 循环继续
            emojiUl.removeChild(emojiUl.firstChild);
        }

        JSON.parse(localStorage.getItem("emoji")).forEach(e => {
            let html = '<li><img src=' + e + ' class="hp-avatar square bigger emoji-img"/></li>';
            emojiUl.insertAdjacentHTML('beforeend', html);
        });

        emojiImgAddEventListener();
    }

    /**
     * 增加表情图片的点击事件
     */
    function emojiImgAddEventListener() {
        let emojiImgArr = document.getElementsByClassName("emoji-img");
        let index = 0;

        while (index < emojiImgArr.length) {
            emojiImgArr.item(index++).addEventListener("click", function (evt) {
                stopBubble(evt)
                // 对象一定要克隆啊，否则会将原来的dom从之前的位置移除
                let emoji = evt.target.cloneNode(true);
                insertHtmlAtCaret(emoji);
                // 插入图片到对话框后，关闭 emoji-board
                let emoji_board = document.getElementById("emoji-board");
                if (emoji_board) {
                    emoji_board.style = "display: none";
                }
            })
        }
    }

    initEmojiBoard();
    addBtnAddEventListener();
    emojiAddEventListener();


    //https://blog.csdn.net/qq_33769914/article/details/93596502
    /**
     * 重要牛逼的博客 如何在div是contentEditable=true的里面获取光标的位置，并在光标位置处插入内容。焦点位置插入指定内容
     */
    var sel,range;
    var textContent;
    //失去焦点时获取光标的位置
    function getblur(){
        sel = window.getSelection();
        range = sel.getRangeAt(0);
        range.deleteContents();
    }

    //在复杂公式的div contenteditable=true的位置获取焦点并插入内容
    function insertHtmlAtCaret(html) {
        html.removeAttribute("class")
        if (window.getSelection) {
            // IE9 and non-IE
            if (sel.getRangeAt && sel.rangeCount) {
                // var el = html.outerHTML;
                // var frag = document.createDocumentFragment(), node, lastNode;
                // while ((node = el.firstChild)) {
                //     lastNode = frag.appendChild(node);
                // }
                range.insertNode(html);
                // Preserve the selection
                // if (lastNode) {
                //     range = range.cloneRange();
                //     range.setStartAfter(lastNode);
                //     range.collapse(true);
                //     sel.removeAllRanges();
                //     sel.addRange(range);
                // }
            }
        } else if (document.selection && document.selection.type != "Control") {
            // IE < 9
            document.selection.createRange().pasteHTML(html);
        }
    }

    document.getElementById("msg").focus()

    var $h = {
        post: function (url, data, callback) {
            // 创建异步对象
            let xhr = new XMLHttpRequest();
            // 设置异步请求方法 为 post
            xhr.open('POST', url, true);

            // 添加http头，发送信息至服务器时内容编码类型为json
            xhr.setRequestHeader("Content-Type", "application/json");
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status == 200 || xhr.status == 304) {
                    if(callback) {
                        callback(this, JSON.parse(xhr.responseText));
                    }
                }
            };
            // 发送数据
            xhr.send(JSON.stringify(data));
        }
    };

</script>
</html>